﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace RoboZip
{
    public class ArgumentParser
    {
        private List<string> files = new List<string>();
        private bool fileNameProcessingIsDone = false;

        public void ParseArguments(ref Job Job, String[] args)
        {
            try
            {
                files.Clear();
                fileNameProcessingIsDone = false;
                Regex regExValidPathname = new Regex(@"^((\\\\[a-zA-Z0-9-]+\\[a-zA-Z0-9`~!@#$%^&(){}'._-]+([ ]+[a-zA-Z0-9`~!@#$%^&(){}'._-]+)*)|([a-zA-Z]:))(\\[^ \\/:*?""<>|]+([ ]+[^ \\/:*?""<>|]+)*)*\\?$");
                Job.ErrorNumber = ErrorNumber.NoError;

                if (args.Count() == 0)
                {
                    ConsoleWriteProgramHints.WriteHeader();
                    ConsoleWriteProgramHints.WriteShortHelp();
                    Job.ErrorNumber = ErrorNumber.NoArguments;
                    Job.ErrorDescription = @"No arguments passed.";
                    return;
                }

                if (args.Count() == 1)
                {
                    if (args[0] == @"/?")
                    {
                        ConsoleWriteProgramHints.WriteHeader();
                        ConsoleWriteProgramHints.WriteFullHelp();
                    }
                    else
                    {
                        Job.ErrorDescription = @"Not enough arguments passed.";
                        ConsoleWriteProgramHints.WriteArgumentError(Job);
                    }
                    Job.ErrorNumber = ErrorNumber.OnlyOneArgument;
                    return;
                }

                if (args.Count() >= 2)
                {
                    Job.SourcePath = args[0];
                    Job.DestinationPath = args[1];
                    Boolean PathName_0_IsValid = regExValidPathname.IsMatch(args[0]);
                    Boolean PathName_1_IsValid = regExValidPathname.IsMatch(args[1]);

                    if (!PathName_0_IsValid)
                    {
                        Job.ErrorNumber = ErrorNumber.Source;
                        Job.ErrorDescription = @"Invalid source.";
                        ConsoleWriteProgramHints.WriteArgumentError(Job);
                        return;
                    }
                    if (!PathName_1_IsValid)
                    {
                        Job.ErrorNumber = ErrorNumber.Destination;
                        Job.ErrorDescription = @"Invalid destination.";
                        ConsoleWriteProgramHints.WriteArgumentError(Job);
                        return;
                    }
                    if (Job.DestinationPath.StartsWith(Job.SourcePath))
                    {
                        if ((Job.SourcePath.Equals(Job.DestinationPath)) ||
                            (Job.DestinationPath[Job.SourcePath.Length].ToString() == @"\"))
                        {
                            Job.ErrorNumber = ErrorNumber.Destination;
                            Job.ErrorDescription = @"Destination within source path.";
                            ConsoleWriteProgramHints.WriteArgumentError(Job);
                            return;
                        }
                    }

                    if (args.Count() == 2)
                    {
                        files.Add("*.*");
                        Job.FileList = files;
                        return;
                    }
                    else
                    {
                        for (int i = 2; i < args.Count(); i++)
                        {
                            String arg = args[i].Trim();
                            int p = arg.IndexOf("/");
                            if (p != 0)
                            {
                                if (fileNameProcessingIsDone == false)
                                {
                                    files.Add(arg);
                                }
                                else
                                {
                                    Job.ErrorNumber = ErrorNumber.FileName;
                                    Job.ErrorDescription = @"All files must be passed before passing options.";
                                    ConsoleWriteProgramHints.WriteArgumentError(Job);
                                    break;
                                }
                            }
                            else
                            {
                                fileNameProcessingIsDone = true;
                                String[] ArgumentSplit = arg.Split(new Char[] { ':' });
                                if (ArgumentSplit[0].ToUpperInvariant() == "/" + ExecutionOptions.MP.ToString("g").ToUpperInvariant())
                                {
                                    CheckOptionMonitoringPeriod(ref Job, ArgumentSplit);
                                }
                                else if (ArgumentSplit[0].ToUpperInvariant() == "/" + ExecutionOptions.START.ToString("g").ToUpperInvariant())
                                {
                                    CheckOptionSTART(ref Job, ArgumentSplit);
                                }
                                else if (ArgumentSplit[0].ToUpperInvariant() == "/" + ExecutionOptions.FNP.ToString("g").ToUpperInvariant())
                                {
                                    CheckOptionFNP(ref Job, ArgumentSplit);
                                }
                                else if (ArgumentSplit[0].ToUpperInvariant() == "/" + ExecutionOptions.DS.ToString("g").ToUpperInvariant())
                                {
                                    CheckOptionDS(ref Job, ArgumentSplit);
                                }
                                else if (ArgumentSplit[0].ToUpperInvariant() == "/" + ExecutionOptions.UD.ToString("g").ToUpperInvariant())
                                {
                                    CheckOptionUD(ref Job, ArgumentSplit);
                                }
                                else
                                {
                                    Job.ErrorNumber = ErrorNumber.OptionUnknown;
                                }
                                if (Job.ErrorNumber != ErrorNumber.NoError)
                                {
                                    break;
                                }
                            }
                            Job.FileList = files;
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public static void CheckOptionMonitoringPeriod(ref Job Job, String[] ArgumentSplit)
        {
            try
            {
                string option = "/MP";
                if (Job.MonitoringPeriod != null)
                {
                    Job.ErrorDescription = @"Option " + option + ": Passed twice.";
                    Job.ErrorNumber = ErrorNumber.OptionPassedTwice;
                    return;
                }
                if (ArgumentSplit.Count() == 1)
                {
                    Job.ErrorDescription = @"Option " + option + ": Missing parameter.";
                    Job.ErrorNumber = ErrorNumber.OptionParameterMissing;
                    return;
                }
                bool result = false;
                MonitoringPeriod period;
                result = Enum.TryParse(ArgumentSplit[1].Trim(), true, out period);
                if (result)
                {
                    Job.MonitoringPeriod = period;
                }
                else
                {
                    Job.ErrorNumber = ErrorNumber.OptionParameterUnknown;
                    Job.ErrorDescription = @"Option " + option + ": Unknown parameter.";
                    ConsoleWriteProgramHints.WriteArgumentError(Job);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public static void CheckOptionSTART(ref Job Job, String[] ArgumentSplit)
        {
            try
            {
                string option = "/START";
                if (Job.Start != null)
                {
                    Job.ErrorDescription = @"Option " + option + ": Passed twice.";
                    Job.ErrorNumber = ErrorNumber.OptionPassedTwice;
                    return;
                }
                if (ArgumentSplit.Count() == 1)
                {
                    Job.ErrorDescription = @"Option " + option + ": Missing parameter.";
                    Job.ErrorNumber = ErrorNumber.OptionParameterMissing;
                    return;
                }

                bool result = false;
                DateTime start;
                result = DateTime.TryParse(ArgumentSplit[1].Trim(), out start);
                if (result)
                {
                    Job.Start = start;
                }
                else
                {
                    Job.ErrorNumber = ErrorNumber.OptionParameterUnknown;
                    Job.ErrorDescription = @"Option " + option + ": Unknown parameter.";
                    ConsoleWriteProgramHints.WriteArgumentError(Job);
                }
            }
            catch (Exception)
            {
                
                throw;
            }
        }

        public static void CheckOptionFNP(ref Job Job, String[] ArgumentSplit)
        {
            try
            {
                string option = "/FNP";
                if (!string.IsNullOrEmpty(Job.FileNamePattern))
                {
                    Job.ErrorDescription = @"Option " + option + ": Passed twice.";
                    Job.ErrorNumber = ErrorNumber.OptionPassedTwice;
                    return;
                }
                if (ArgumentSplit.Count() == 1)
                {
                    Job.ErrorDescription = @"Option " + option + ": Missing parameter.";
                    Job.ErrorNumber = ErrorNumber.OptionParameterMissing;
                    return;
                }
                if (ArgumentSplit[1].IndexOfAny(Path.GetInvalidFileNameChars()) != -1)
                {
                    Job.ErrorDescription = @"Option " + option + ": Invalid filename.";
                    Job.ErrorNumber = ErrorNumber.FileName;
                    return;                    
                }
                else
                {
                    Job.FileNamePattern = ArgumentSplit[1];
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public static void CheckOptionUD(ref Job Job, String[] ArgumentSplit)
        {
            try
            {
                string option = "/UD";
                if (Job.UpdateDestination != null)
                {
                    Job.ErrorDescription = @"Option " + option + ": Passed twice.";
                    Job.ErrorNumber = ErrorNumber.OptionPassedTwice;
                    return;
                }
                if (ArgumentSplit.Count() > 1)
                {
                    Job.ErrorDescription = @"Option " + option + ": Missing parameter.";
                    Job.ErrorNumber = ErrorNumber.OptionParameterUnknown;
                    return;
                }
                Job.UpdateDestination = true;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public static void CheckOptionDS(ref Job Job, String[] ArgumentSplit)
        {
            try
            {
                string option = "/DS";
                if (Job.DeleteSource != null)
                {
                    Job.ErrorDescription = @"Option " + option + ": Passed twice.";
                    Job.ErrorNumber = ErrorNumber.OptionPassedTwice;
                    return;
                }
                if (ArgumentSplit.Count() > 1)
                {
                    Job.ErrorDescription = @"Option " + option + ": Parameter unknown.";
                    Job.ErrorNumber = ErrorNumber.OptionParameterUnknown;
                    return;
                }
                Job.DeleteSource = true;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public static String RemoveDublicateWhitespace(String TestString)
        {
            Regex regex = new Regex(@"[ ]{2,}", RegexOptions.None);
            return regex.Replace(TestString, @" ");
        }

    }


}
